package typedefault

import (
	"bufio"
	"bytes"
	"codejy/globalfunc"
	"codejy/store"
	"codejy/structdef"
	"fmt"
	"golang.org/x/text/encoding/simplifiedchinese"
	"io"
	"os"
	"os/exec"
	"runtime"
	"sync"
)

// key 实例地址,value 值

type Cmd struct {
	Addr        string
	ThreadStore *structdef.ThreadStore
}

func (f *Cmd) CreateTypeDefault(addr string, threadStore *structdef.ThreadStore, parameter ...string) {
	fUtil := &Cmd{
		Addr:        addr,
		ThreadStore: threadStore,
	}
	store.AddTypeDefaultInstanceStore(threadStore, f.GetTypeName(), addr, fUtil)
}
func (f *Cmd) GetData() interface{} {
	return nil
}
func (a *Cmd) ToStr() string {
	return ""
}
func (a *Cmd) ParameterProcessing(parameter []string) []string {
	return parameter
}

// 执行windwos命令
func (a *Cmd) ExecNotOut(parameter string) {
	value, b := GetTypeDefaultValue[[]string](a.ThreadStore, "List", parameter)
	if !b {
		panic("参数错误")
	}
	a.CommandNotOut(value...)
}

// 执行windwos命令,返回结果
func (a *Cmd) Exec(parameter string) string {
	value, b := GetTypeDefaultValue[[]string](a.ThreadStore, "List", parameter)
	if !b {
		panic("参数错误")
	}
	command, s := a.Command(value...)
	if s != "" {
		panic(s)
	}
	return command
}

func (f *Cmd) GetDef() string {
	return `
		@CodeManual(def="Cmd.xx",des="调用共享元素")
		type share Cmd{
			def init{
				core&CreateTypeDefault()
			}
		
			@CodeManual(def="Cmd.exec($args)",des="执行命令,不返回结果,会打印到控制台")
			def exec{
				core&ExecNotOut($args)
			}
			@CodeManual(def="Cmd.execTo($args)",des="执行命令,返回结果")
			def execTo{
				to core&Exec($args)
			}		
		}
	`
}

func init() {
	RegisterTypeDefault(&Cmd{})
}
func (f *Cmd) GetTypeName() string {
	return "Cmd"
}

type Charset string

const (
	UTF8    = Charset("UTF-8")
	GB18030 = Charset("GB18030")
)

// 解决执行bat文件时返回的内容是乱码
func ConvertByte2String(byte []byte) string {
	var charset Charset
	switch runtime.GOOS {
	case "windows":
		charset = GB18030
	case "linux":
		charset = UTF8
	default:
		charset = UTF8
	}
	var str string
	switch charset {
	case GB18030:
		decodeBytes, _ := simplifiedchinese.GB18030.NewDecoder().Bytes(byte)
		str = string(decodeBytes)
	case UTF8:
		fallthrough
	default:
		str = string(byte)
	}

	return str
}

func (a *Cmd) Command(arg ...string) (string, string) {
	arg = RemoveListStr(arg)
	arg = IsWindows(arg)
	cmd1 := exec.Command(arg[0], arg[1:]...)
	return PipeInOut(cmd1)
}

func (a *Cmd) CommandNotOut(arg ...string) {
	arg = RemoveListStr(arg)
	arg = IsWindows(arg)
	cmd1 := exec.Command(arg[0], arg[1:]...)
	a.PipeNotOut(cmd1)
}

// 管道输出
func (a *Cmd) PipeNotOut(cmd *exec.Cmd) {
	sbStdErr := bytes.NewBuffer(nil)
	cmd.Stderr = sbStdErr
	//实时获取： 需要使用 Cmd 结构的 StdoutPipe() 方法创建一个管道连接到命令执行的输出
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		fmt.Fprintln(os.Stderr, "error=>", err.Error())
	}
	cmd.Start() // Start开始执行c包含的命令，但并不会等待该命令完成即返回。Wait方法会返回命令的返回状态码并在命令返回后释放相关的资源。
	readerStdOut := bufio.NewReader(stdout)
	//然后用 for 循环从管道中实时读取命令执行的输出并打印到终端
	group := globalfunc.ExecuteFunc("GetThreadAllWait").(*sync.WaitGroup)
	group.Add(1)
	go func() {
		defer func() {
			if err1 := recover(); err1 != nil {
				store.PrintExecuteCodeRecordLog(a.ThreadStore, err1)
				os.Exit(1)
			}
		}()
		for {
			line, err2 := readerStdOut.ReadString('\n')
			line = ConvertByte2String([]byte(line))
			fmt.Print(line)
			if err2 != nil || io.EOF == err2 {
				errMsg := string(sbStdErr.Bytes())
				errMsg = ConvertByte2String([]byte(errMsg))
				if errMsg != "" { //错误信息
					fmt.Printf("#error:%s\n", errMsg)
				}
				break
			}
		}
		cmd.Wait()
		group.Done()
	}()

}
func PipeInOut(cmd *exec.Cmd) (string, string) {
	var sb bytes.Buffer
	sbStdErr := bytes.NewBuffer(nil)
	cmd.Stderr = sbStdErr
	//实时获取： 需要使用 Cmd 结构的 StdoutPipe() 方法创建一个管道连接到命令执行的输出
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		fmt.Fprintln(os.Stderr, "error=>", err.Error())
	}
	cmd.Start() // Start开始执行c包含的命令，但并不会等待该命令完成即返回。Wait方法会返回命令的返回状态码并在命令返回后释放相关的资源。
	readerStdOut := bufio.NewReader(stdout)
	//然后用 for 循环从管道中实时读取命令执行的输出并打印到终端
	for {
		line, err2 := readerStdOut.ReadString('\n')
		line = ConvertByte2String([]byte(line))
		sb.WriteString(line)
		if err2 != nil || io.EOF == err2 {
			errMsg := string(sbStdErr.Bytes())
			errMsg = ConvertByte2String([]byte(errMsg))
			if errMsg != "" {
				return "", errMsg
			}
			break
		}
	}
	cmd.Wait()
	return sb.String(), ""

}

// 去除list中每个元素的""
func RemoveListStr(list []string) []string {
	var newlist []string
	for _, v := range list {
		//判断是否是"
		if v[0:1] == "\"" && v[len(v)-1:] == "\"" {
			v = v[1 : len(v)-1]
			newlist = append(newlist, v)
		} else {
			newlist = append(newlist, v)
		}
	}
	return newlist
}

// 判断是否是windwos系统,如果是则在list中添加powershell
func IsWindows(list []string) []string {
	if runtime.GOOS == "windows" {
		list = append([]string{"PowerShell"}, list...)
	}
	return list

}
